//
//  LTCoreDataHelper.m
//  GroceryDude
//
//  Created by 陈记权 on 11/9/15.
//  Copyright © 2015 陈记权. All rights reserved.
//

#import "LTCoreDataHelper.h"

@implementation LTCoreDataHelper

- (instancetype)init
{
    self = [super init];
    if (self) {
        NSLog(@"Running %@ '%s'", self.class, sel_getName(_cmd));
        _model = [NSManagedObjectModel mergedModelFromBundles:nil];
        _coordinator = [[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:_model];
        _context = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_context setPersistentStoreCoordinator:_coordinator];
    }
    return self;
}

- (void)loadStore
{
    NSLog(@"Running %@ '%s'", self.class, sel_getName(_cmd));
    
    if (_store) {
        return;
    }
    
    BOOL userMigrationManager = YES;
    if (userMigrationManager && [self isMigrationNecessaryForStore:[self storeURL]]) {
        [self performBackgroundManagedMigrationForStore:[self storeURL]];
        
        return;
    }
    
    NSDictionary *options = @{
                              NSMigratePersistentStoresAutomaticallyOption : @YES,
                              NSInferMappingModelAutomaticallyOption : @NO
                              };
    
    NSError *error = nil;
    if (!_store) {
        _store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType
                                            configuration:nil
                                                      URL:[self storeURL]
                                                  options:options
                                                    error:&error];
    }
    if (!_store) {
        NSLog(@"Failed to add store. Error:%@", error);
    } else {
        NSLog(@"Successfully add store: %@", _store);
    }
}

- (void)setupCoreData
{
     NSLog(@"Running %@ '%s'", self.class, sel_getName(_cmd));
    [self loadStore];
}


#pragma mark -
#pragma mark - Migrate by Migration Manager

- (void)performBackgroundManagedMigrationForStore:(NSURL *)storeURL
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        BOOL done = [self migrateStore:storeURL];
        
        if (done) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSError *error;
                _store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self storeURL] options:nil error:&error];
                
                if (!_store) {
                    NSLog(@"Falied to add a migrated store, Error:%@", error);
                } else {
                    NSLog(@"Successfully added a migrated store:%@", _store);
                }
            });
        }
       
    });
}


- (BOOL)migrateStore:(NSURL *)sourceStore
{
    BOOL success = NO;
    NSError *error;
    
    
    NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator
                                    metadataForPersistentStoreOfType:NSSQLiteStoreType
                                    URL:sourceStore
                                    options:nil
                                    error:&error];
    
    NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil
                                                                    forStoreMetadata:sourceMetadata];
    
    NSManagedObjectModel *destinModel = _model;
    
    NSMappingModel *mappingModel = [NSMappingModel mappingModelFromBundles:nil forSourceModel:sourceModel destinationModel:destinModel];
    
    if (mappingModel) {
        NSError *error;
        
        NSMigrationManager *migrationManager = [[NSMigrationManager alloc]initWithSourceModel:sourceModel
                                                                             destinationModel:destinModel];
        
        [migrationManager addObserver:self forKeyPath:@"migrationProgress"
                              options:NSKeyValueObservingOptionNew
                              context:NULL];
        
        NSURL *destinStore = [[self applicationStoresDirectory] URLByAppendingPathComponent:@"Temp.sqlite"];
        
        success = [migrationManager migrateStoreFromURL:sourceStore
                                                   type:NSSQLiteStoreType
                                                options:nil
                                       withMappingModel:mappingModel
                                       toDestinationURL:destinStore
                                        destinationType:NSSQLiteStoreType
                                     destinationOptions:nil
                                                  error:&error];
        
        if (success) {
            if ([self replaceStore:sourceStore withStore:destinStore]) {
                NSLog(@"Successfully migration %@ to the current model", sourceStore.path);
                [migrationManager removeObserver:self forKeyPath:@"migrationProgress"];
            }
        } else {
            NSLog(@"Failed Migration:%@", error);
        }
    } else {
        NSLog(@"Failed migration:mapping model is null");
    }
    return YES;
}

- (BOOL)isMigrationNecessaryForStore:(NSURL *)storeURL
{
    if (![[NSFileManager defaultManager]fileExistsAtPath:[self storeURL].path]) {
        NSLog(@"Skipped Migration: Source database missing");
        return NO;
    }
    
    NSError *error;
    
    NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator
                                    metadataForPersistentStoreOfType:NSSQLiteStoreType
                                    URL:storeURL
                                    options:nil
                                    error:&error];
    
    NSManagedObjectModel *destinationModel = _coordinator.managedObjectModel;
    
    if ([destinationModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata]) {
        NSLog(@"Skipped Migration: Source is already cpmpatible");
        return NO;
    }
    return YES;
}

- (BOOL) replaceStore:(NSURL *)old withStore:(NSURL *)new
{
    BOOL success;
    NSError *error;
    
    if ([[NSFileManager defaultManager] removeItemAtURL:old error:&error]) {
        error = nil;
        if ([[NSFileManager defaultManager]moveItemAtURL:new toURL:old error:&error]) {
            success = YES;
        } else {
            NSLog(@"Fialed to re-home new store with error:%@", error);
        }
    } else {
        NSLog(@"Failed to remove old store:%@ Error: %@", old, error);
    }
    return success;
}

- (void)saveContext
{
    NSLog(@"Running %@ '%s'", self.class, sel_getName(_cmd));
    
    if ([_context hasChanges]) {
        NSError *error = nil;
        if ([_context save:&error]) {
            NSLog(@"_context Saved changes to persistent store");
        } else {
            NSLog(@"Failed to sace _context: %@", error);
        }
    } else {
        NSLog(@"Skipped _context save , there is not changes!");
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"migrationProgress"]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            float progress = [[change objectForKey:NSKeyValueChangeNewKey] floatValue];
            NSLog(@"Migrated:%@", @(progress));
        });
    }
}

#pragma mark 
#pragma mark - Data Source URL

- (NSURL *)applicationStoresDirectory
{
    NSLog(@"Running %@ '%s'", self.class, sel_getName(_cmd));
    
    NSURL *storesDirectory = [[NSURL fileURLWithPath:[self applicationDocumentsDirectory]] URLByAppendingPathComponent:@"Stores"];
    
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager fileExistsAtPath:[storesDirectory path]]) {
        NSError *error = nil;
        if ([fileManager createDirectoryAtURL:storesDirectory
                  withIntermediateDirectories:YES
                                   attributes:nil error:&error]) {
            NSLog(@"Successfully created stores directory");
        } else {
            NSLog(@"failed to create stores direcroty; %@", error);
        }
    }
    
    return storesDirectory;
}

- (NSURL *)storeURL
{
    return [[self applicationStoresDirectory]URLByAppendingPathComponent:storeFileName];
}

- (NSString *)applicationDocumentsDirectory
{
    NSLog(@"Running %@ '%s'", self.class, sel_getName(_cmd));
    
    return [NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) lastObject];
}

@end
